/***************************************
 * <p>Title: MD5ValueProcessing</p>
 * <p>Description: 对文件进行MD5加签 </p>
 * <p>Company: Union Mobile Pay Ltd.</p>
 ***************************************/
package com.umpay.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import sun.nio.ch.DirectBuffer; 
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import org.apache.commons.logging.Log;
import org.apache.log4j.Logger;


/***************************************
 * <p>Title: MD5ValueProcessing</p>
 * <p>Description: 对文件进行MD5加签 </p>
 * <p>Company: Union Mobile Pay Ltd.</p>
 * @author  Zengfu Jiang
 * @date 2014年8月20日
 ***************************************/
public class MD5ValueProcessing {
	private static final Logger log = Logger.getLogger(MD5ValueProcessing.class);
	protected static char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6','7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
	
	protected static MessageDigest messageDigest = null;
	static {
		try {
			messageDigest = MessageDigest.getInstance("MD5");
		} catch (NoSuchAlgorithmException nsaex) {
			log.debug("文件MD5加签类(MD5ValueProcessing)初始化失败，MessageDigest不支持MD5!");
			nsaex.printStackTrace();
		}
	}

	public static void main(String[] args) throws IOException {
		long begin = System.currentTimeMillis();

		File big = new File("D:/tmp/100001.txt");
	    String md5 = getFileMD5String(big);
		//String md5 = getMD5String("a");
		long end = System.currentTimeMillis();
		log.debug("md5:" + md5 + " time:" + ((end - begin) / 1000) + "s");
	}

	public static String getFileMD5String(File file) throws IOException {
		FileInputStream in = null;
		FileChannel ch = null;
		try {
			in = new FileInputStream(file);
			ch = in.getChannel();
			
			//700000000 bytes are about 670M
			int maxSize=700000000;
			
			long startPosition=0L;
			long step=file.length()/maxSize;
			
			if(step == 0){
				MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, 0,file.length());
				messageDigest.update(byteBuffer);
				unmap(byteBuffer);
				return bufferToHex(messageDigest.digest());
			}
			
			for(int i=0;i<step;i++){
				MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, startPosition,maxSize);
				messageDigest.update(byteBuffer);
				startPosition+=maxSize;
				unmap(byteBuffer);
			}
			
			if(startPosition==file.length()){
				return bufferToHex(messageDigest.digest());
			}

			MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, startPosition,file.length()-startPosition);
			messageDigest.update(byteBuffer);
			unmap(byteBuffer);
		} catch (Exception e) {
			log.error("文件MD5加签发生异常");
		}finally {
			if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                	log.error("关闭IO发生异常");
                }
            }
            if (ch !=null ) {
                try {
                	ch.close();
                } catch (IOException e) {
                	log.error("关闭IO发生异常");
                }
            }
		}
			
		return bufferToHex(messageDigest.digest());
	}

	public static String getMD5String(String s) {
		return getMD5String(s.getBytes());
	}

	public static String getMD5String(byte[] bytes) {
		messageDigest.update(bytes);
		return bufferToHex(messageDigest.digest());
	}

	private static String bufferToHex(byte bytes[]) {
		return bufferToHex(bytes, 0, bytes.length);
	}

	private static String bufferToHex(byte bytes[], int m, int n) {
		StringBuffer stringbuffer = new StringBuffer(2 * n);
		int k = m + n;
		for (int l = m; l < k; l++) {
			appendHexPair(bytes[l], stringbuffer);
		}
		return stringbuffer.toString();
	}

	private static void appendHexPair(byte bt, StringBuffer stringbuffer) {
		char c0 = hexDigits[(bt & 0xf0) >> 4];
		char c1 = hexDigits[bt & 0xf];
		stringbuffer.append(c0);
		stringbuffer.append(c1);
	}

	public static boolean checkPassword(String password, String md5PwdStr) {
		String s = getMD5String(password);
		return s.equals(md5PwdStr);
	}

	private static void unmap(MappedByteBuffer var0) {
    	sun.misc.Cleaner  var1 = ((DirectBuffer)var0).cleaner();
        if (var1 != null) {
            var1.clean();
        }
    }
	
}
